#include "Multitree.h"
#include <assert.h>
namespace SymCe{

	Multitree * taylorize(Multitree * tree, Number center, unsigned short int terms, char * variable){
		//tree must have only one variable (and that variable must be the "variable")
		//but - it could be done in other way
		Multitree * x_power;	//(x-a)^n
		Multitree * XminusA;	//x-a
		Multitree * result;		//whole thing
		Multitree * current_derivative; //f()'
		Number current_denominator; //n!
		Multitree * added;		//current term
		Multitree * temp1;		//Derivative with replaced variable
		Multitree * temp2;
		Multitree * cent_num;	//center, but as a multitree
		Number numderiv;		//Numeric derivative
		ListOfChilds * new_child;

		int term_number;	//int? Why not Integer

		//prepare some helper parts

		cent_num = give_me_empty_tree();
		cent_num->symbol.type = NUMBER;
		cent_num->symbol.number = center;
		cent_num->childs = NULL;
		
		//the tree we need to create is + with subtrees in common form: derivative * denominator * (x-a)^power
		XminusA = give_me_tree_with_two_childs();
		XminusA->symbol.type = OPERATOR;
		XminusA->symbol.oper = PLUS;
		XminusA->childs->inverted = false;
		XminusA->childs->subtree->symbol.type=VARIABLE;
		XminusA->childs->subtree->symbol.name = new char[2];
		strcpy(XminusA->childs->subtree->symbol.name, "X");
		XminusA->childs->next->inverted = true;
		XminusA->childs->next->subtree->symbol.type = NUMBER;
		XminusA->childs->next->subtree->symbol.number = center;

		//x_power = (x-a)^0)

		x_power = give_me_tree_with_two_childs();
		x_power->symbol.type = OPERATOR;
		x_power->symbol.oper = POWER;
		x_power->childs->inverted = false;
		x_power->childs->next->inverted = false;
		delete_tree(x_power->childs->subtree);
		x_power->childs->subtree = XminusA;
		x_power->childs->next->subtree->symbol.type = NUMBER;
		x_power->childs->next->subtree->symbol.number = Number(0);
		XminusA = NULL;





		//denominator (0! = 1)


		//current_derivative
		current_derivative = copy_tree(tree);

		
		temp1 = replace_variable_tree(current_derivative, variable, cent_num);
		temp2 = canonicalize_tree(temp1);
		delete_tree(temp1);
		temp1 = NULL;
		try{
			numderiv = numeric(temp2);
		}catch(...){	//catch everything, destroy structures and throw the exception up
			//we caught either division by zero or unknow variable. In both cases we are not really able to continue
			delete_tree(x_power);
			delete_tree(current_derivative);
			delete_tree(cent_num);
			delete_tree(temp2);
			throw;
		}

		delete_tree(temp2);
		temp2 = NULL;
		//prepare first term
		added = give_me_tree_with_two_childs();
		added->symbol.type = OPERATOR;
		added->symbol.oper = MULTIPLY;
		//third part - x_power
		added->childs->next->next = give_me_empty_child();
		added->childs->next->next->next = NULL;
		added->childs->next->next->inverted = false;
		added->childs->next->next->subtree = copy_tree(x_power);
		//second part - derivative - numeric
		added->childs->next->inverted = false;
		added->childs->next->subtree->symbol.type = NUMBER;
		added->childs->next->subtree->symbol.number = numderiv;
		//firtst part - denominator
		added->childs->inverted = true;
		added->childs->subtree->symbol.type = NUMBER;
		added->childs->subtree->symbol.number = current_denominator;
		
		//Added is now the first term we need
		if (terms == 1){
			delete_tree(x_power);
			delete_tree(current_derivative);
			delete_tree(cent_num);
			return added;
		}
		
		result = give_me_empty_tree();
		result->symbol.type = OPERATOR;
		result->symbol.oper = PLUS;
		result->childs = give_me_empty_child();
		result->childs->next = NULL;
		result->childs->inverted = false;
		result->childs->subtree = added;

		for(term_number = 1; term_number < terms; ++term_number){
			//make next derivative
			temp1 = diff_tree(current_derivative, variable);
			delete_tree(current_derivative);
			current_derivative = canonicalize_tree(temp1);
			delete_tree(temp1);
			temp1=NULL;
			//increase n in (x-a)^n
			x_power->childs->next->subtree->symbol.number = x_power->childs->next->subtree->symbol.number + 1;
			assert(x_power->childs->next->subtree->symbol.number == term_number);
			//increase factorial in denominator
			current_denominator = current_denominator * term_number;

			//prepare numeric derivative
			temp1 = replace_variable_tree(current_derivative, variable, cent_num);
			temp2 = canonicalize_tree(temp1);
			delete_tree(temp1);
			temp1 = NULL;

			try{
				numderiv = numeric(temp2);
			}catch(...){
				//We delete again everything (including result) and throw exception upwards
				//The code should not get here, because we would get error before (at first call to numeric)
				delete_tree(x_power);
				delete_tree(current_derivative);
				delete_tree(cent_num);
				delete_tree(temp2);
				delete_tree(result);
				throw;
			}

			delete_tree(temp2);
			temp2 = NULL;


			//Added (copied code!)
			added = give_me_tree_with_two_childs();
			added->symbol.type = OPERATOR;
			added->symbol.oper = MULTIPLY;
			//third part - x_power
			added->childs->next->next = give_me_empty_child();
			added->childs->next->next->next = NULL;
			added->childs->next->next->inverted = false;
			added->childs->next->next->subtree = copy_tree(x_power);
			//second part - derivative - numeric
			added->childs->next->inverted = false;
			added->childs->next->subtree->symbol.type = NUMBER;
			added->childs->next->subtree->symbol.number = numderiv;
			//firtst part - denominator
			added->childs->inverted = true;
			added->childs->subtree->symbol.type = NUMBER;
			added->childs->subtree->symbol.number = current_denominator;
			
			//Now connect it to result
			new_child = give_me_empty_child();
			new_child->inverted = false;
			new_child->subtree = added;
			new_child->next = result->childs;
			result->childs = new_child;
			//we should probably make result canonical, but I think it is not necessary now
		}
		delete_tree(x_power);
		delete_tree(current_derivative);
		delete_tree(cent_num);
		temp1 = canonicalize_tree(result);
		result = temp1;
		temp1 = NULL;
		return result;
	}
}




		
